// By: wlmwang
// Date: Jul 15 2019

#ifndef ANT_SIGNAL_SERVER_H_
#define ANT_SIGNAL_SERVER_H_

#include "Macros.h"
#include "CallbackForward.h"

#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <memory>
#include <functional>
#include <signal.h> 	// SIGTERM,...

namespace annety
{
class EventLoop;
class Channel;

// When a signal occurs, which thread will receive?
// 1. If it is an abnormally generated signal (SIGPIPE, SIGEGV...), 
// 	  only the thread that generated the exception is received and 
// 	  processed.
// 2. If it is an internal signal generated by `pthread_kill`, only 
// 	  the target thread specified in the `pthread_kill` parameter is 
// 	  received and processed.
// 3. If it is a signal generated by the external use of the `kill` 
// 	  command, such as shell signals (SIGINT, SIGHUP...), it will 
// 	  traverse all threads until it finds a thread that does not 
// 	  block the signal, and then call it to process (Generally the
//	  main thread first). Note that only one thread can receive.

// Does each thread have its own mask and action?
// 1. Each thread has its own signal mask, but all threads share the 
// 	  signal action of the process.
// 2. You can call `pthread_sigmask`(NOT `sigmask`) in the thread to 
// 	  determine which signals this thread is blocking. But you cannot 
// 	  call `sigaction` to set each thread to have a different processor.
// 3. The sub-thread mask will inherit the main thread settings.

// Signal server wrapper of OS signal.
//
// NOTICE: It CAN ONLY BE CREATED BY MAIN THREAD!!!
// Because signals are complex in multithreading (see above for details), 
// in order to simplify, we have to design them in this way.
class SignalServer
{
public:
	explicit SignalServer(EventLoop* loop);
	~SignalServer();

	// Must be called in own loop thread(the main thread).
	bool ismember_signal(int signo);

	// *Thread safe*
	void revert_signal();
	void ignore_signal(int signo);
	void delete_signal(int signo);
	void add_signal(int signo, SignalCallback cb);

private:
	// *Not thread safe*, but run in own loop thread.
	void handle_read();
	void add_signal_in_own_loop(int signo, SignalCallback cb);
	void delete_signal_in_own_loop(int signo);
	void revert_signal_in_own_loop();

	using SignalSet = std::unordered_set<int>;
	using SignalMap = std::unordered_map<int, SignalCallback>;

private:
	EventLoop* owner_loop_{nullptr};
	bool calling_signal_functor_{false};

	SelectableFDPtr signal_socket_;
	std::unique_ptr<Channel> signal_channel_;

	SignalMap signals_;
};

}	// namespace annety

#endif	// ANT_SIGNAL_SERVER_H_
